(use-modules (dsv))
(use-modules ((rnrs)
              :version (6)
              #:prefix rnrs:))

;; The important procedures and other items are (copied from the docs of guile-dsv):

;; dsv->scm [port [delimiter]] [#:format='unix] [#:comment-prefix='default]
;; dsv-string->scm string [delimiter] [#:format='unix] [#:comment-symbol='default]
;; scm->dsv list [port [delimiter]] [#:format='unix]
;; scm->dsv-string list [delimiter] [#:format='unix]
;; guess-delimiter string [known-delimiters]
;; set-debug! enabled?
;; dsv-parser-error

;; =======
;; HELPERS
;; =======
(define displayln
  (lambda (sth)
    (display (simple-format #f "~a\n" sth))))

(define (map* proc lst)
  (cond [(null? lst) '()]
        [(pair? (car lst))
         (cons (map* proc (car lst))
               (map* proc (cdr lst)))]
        [else
         (cons (proc (car lst))
               (map* proc (cdr lst)))]))

(define (stringify* lst)
  (map* (lambda (val)
          (cond
           [(number? val) (number->string val)]
           [(string? val) val]
           [else (simple-format #f "~s" val)]))
        lst))


;; =============================
;; LIBRARY INTERFACE ABSTRACTION
;; =============================
(define* (read-dsv-from-file
          file-path
          #:optional (delimiter #\,)
          #:key
          (format 'unix)
          (comment-prefix 'default)
          (encoding "UTF-8"))
  (call-with-input-file file-path
    (lambda (port)
      (set-port-encoding! port encoding)
      (dsv->scm port
                delimiter
                #:format format
                #:comment-prefix comment-prefix))))


(define* (read-dsv-from-string input
                               #:optional (delimiter #\,)
                               #:key
                               (format 'unix)
                               (comment-prefix 'default))
  (dsv-string->scm input
                   delimiter
                   #:format format
                   #:comment-prefix comment-prefix))


(define* (write-scm-dsv-to-file scm-output file-path
                                #:optional (delimiter #\,)
                                #:key
                                (format 'unix)
                                (comment-prefix 'default)
                                (encoding "UTF-8"))
  ;; For some unknown reason scm->dsv expects everything inside the
  ;; list to be strings already. This means we need to convert to
  ;; strings before giving the data to scm->dsv.

  ;; WARNING: There is no representation for symbols in DSV files, so
  ;; symbols and strings are not distinguished within such a
  ;; file. That means the conversion is lossy for some types of data.
  (call-with-output-file file-path
    (lambda (port)
      (rnrs:assert (list? scm-output))
      (set-port-encoding! port encoding)
      (scm->dsv (stringify* scm-output)
                port
                delimiter
                #:format format))))


(define* (write-scm-dsv-to-string scm-output
                                  #:optional (delimiter #\,)
                                  #:key
                                  (format 'unix)
                                  (comment-prefix 'default))
  (scm->dsv-string (stringify* scm-output)
                   delimiter
                   #:format format))


;; =============
;; USAGE EXAMPLE
;; =============
(define (main)
  (displayln "read from file")
  (displayln
   (read-dsv-from-file "example-in.csv"))

  (displayln "read from string")
  (displayln
   (read-dsv-from-string
    (string-join '("1,2,3,4" "5,6,7,8" "9,10,\"a\",\"b\"")
                 "\n")))

  (displayln "write to file")
  (displayln
   (write-scm-dsv-to-file
    '((1 2 3 4) (5 6 7 8) (9 10 "a" "b" c))
    "example-out.csv"))

  (displayln "write to string")
  (displayln
   (write-scm-dsv-to-string '((1 2 3 4) (5 6 7 8) (9 10 "a" "b")))))

(main)
